
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <assert.h>
//#include <gc_allocator.h>

#include "Boolean.h"
#include "Completion.h"
#include "Expression.h"
#include "ForStatement.h"
#include "NameValue.h"
#include "NameValueList.h"
#include "Null.h"
#include "Scope.h"
#include "String.h"
#include "Value.h"

ForStatement::ForStatement(NameValueList* vlist,
        Expression* initializer, Expression* condition,
        Expression* update, Statement* statement)
    : m_vlist(vlist), m_initializer(initializer), m_condition(condition),
    m_update(update), m_statement(statement)
{}

Value* ForStatement::execute_internal(Scope* scope) const
{
    // TODO: this is rather perverted.
    // Ecma 12.6.3
    // 0. Evaluate NameValueList
    if (m_vlist)
    {
        for (int i = 0; i < m_vlist->size(); i++)
        {
            // 1. Evaluate identifier as in 11.1.2, which references 10.1.4
            Value* id = scope->lookup(m_vlist->get(i)->get_name());
            Expression* exp = m_vlist->get(i)->get_value();

            if (exp)
            {
                // 2. Evaluate initializer
                // 3. Call getValue on initer
                Value* initer = exp->evaluate(scope)->getValue();
                CHECK_EXCEPTION(initer);

                // 4. Call putValue(id, initer)
                id->putValue(initer);
            }

            // 5. Return a string value containing the same sequence of
            // characters as in the Identifier.
            // TODO: huh?
        }
    }

    // 1. if the initializer is not present go to step 4
    if (m_initializer)
    {
        // 2. Evaluate initializer
        // 3. Call getValue on the result (this value is not used)
        m_initializer->evaluate(scope)->getValue();
    }

    // 4. Let V = empty 
    Value* v = Null::null;

    while (true)
    {
        // 5. If the second condition is not present, go to step 10.
        if (m_condition)
        {
            // 6. Evaluate condition
            // 7. Call getValue on that result
            // 8. Call toBoolean on that result
            Boolean* cnd = m_condition->evaluate(scope)->getValue()->toBoolean();

            // 9. If cnd is false, go to step 19.
            if (!cnd->isTrue())
            {
                // 19. Return normal, V, empty
                return new Completion(CT_NORMAL, v, Completion::empty);
            }
        }

        // 10 evaluate statement.
        Value* stmt = m_statement->execute(scope);

        // 11. if stmt is not empty, let v = it's value
        if (stmt->completion()->value()->getType() != VT_NULL)
        {
            v = stmt->completion()->value();
        }

        // 12. If result.type is break and result.target is in the
        // current label set, go to step 19. We don't have to do
        // this, because Statement already does it for us.

        // 13. If result.type is continue and result.target is in the
        // current label set, go to step 15
        if (! (stmt->completion()->type() == CT_CONTINUE
                && this->hasLabel(stmt->completion()->target())))
        {
            // 14. If result is an abrupt completion (not normal), return
            // stmt
            if (stmt->completion()->type() != CT_NORMAL)
            {
                return stmt;
            }

        }

        // 15. if update is not present, go to step 5
        if (m_update)
        {
            // 16. Evaluate update
            // 17. call getValue. This value is not used
            m_update->evaluate(scope)->getValue();
        }

        // 18. Go to step 5.
    }

    // We should never get here
    assert(false);
    return new Completion(CT_NORMAL, v, Completion::empty);
}

void ForStatement::getDeclaredVariables(std::vector<String*>& ids) const
{
    if (m_vlist)
    {
        for (int i = 0; i < m_vlist->size(); i++)
        {
            ids.push_back(m_vlist->get(i)->get_name());
        }
    }

    m_statement->getDeclaredVariables(ids);
}

